﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Globalization;
using System.Linq;
using Microsoft.Internal;
using Microsoft.Internal.Collections;

namespace System.ComponentModel.Composition.ReflectionModel
{
    internal abstract class ImportingItem
    {
        private readonly ContractBasedImportDefinition _definition;
        private readonly ReflectionItem _item;
        private readonly ImportType _importType;

        protected ImportingItem(ContractBasedImportDefinition definition, ReflectionItem item)
        {
            Assumes.NotNull(definition, item);

            _definition = definition;
            _item = item;
            _importType = new ImportType(_item.ReturnType);
        }

        public ContractBasedImportDefinition Definition
        {
            get { return _definition; }
        }

        public ImportType ImportType
        {
            get { return _importType; }
        }

        public object CastExportsToImportType(Export[] exports)
        {
            if (this.Definition.Cardinality == ImportCardinality.ZeroOrMore)
            {
                return CastExportsToCollectionImportType(exports);
            }
            else
            {
                return CastExportsToSingleImportType(exports);
            }
        }

        private object CastExportsToCollectionImportType(Export[] exports)
        {
            Assumes.NotNull(exports);

            if (this.ImportType.IsLazy)
            {   // ie ExportCollection, ExportCollection<T> or ExportCollection<T, TMetadataView>

                return ExportServices.CreateStronglyTypedExportCollectionFromExports(this.ImportType.LazyType, exports);
            }

            IList list = CollectionServices.CreateGenericList(this.ImportType.ElementType);

            foreach (Export export in exports)
            {
                object value = Cast(this._importType.ElementType, export);

                list.Add(value);
            }

            return list;
        }

        private object CastExportsToSingleImportType(Export[] exports)
        {
            Assumes.NotNull(exports);
            Assumes.IsTrue(exports.Length < 2);

            if (exports.Length == 0)
            {   
                return null;
            }
            
            if (this.ImportType.IsLazy)
            {   // ie Export, Export<T>, or Export<T, TMetadataView>

                return ExportServices.CreateExport(this.ImportType.LazyType.ElementExportType, exports[0]);
            }

            return Cast(this._importType.Type, exports[0]);
        }

        private object Cast(Type type, Export export)
        {
            // TODO: Need to catch CompositionException to provide
            // additional information about what member we're setting
            // and the current dependency graph.
            object value = export.GetExportedObject();

            object result;
            if (!ContractServices.TryCast(type, value, out result))
            {
                throw new ComposablePartException(
                    CompositionErrorId.ReflectionModel_ImportNotAssignableFromExport,
                    String.Format(CultureInfo.CurrentCulture,
                        Strings.ReflectionModel_ImportNotAssignableFromExport,
                        export.ToElement().DisplayName,
                        type.FullName),
                    Definition.ToElement());
            }

            return result;
        }
    }
}